/* 
 * File:   main.cpp
 * Author: Luciano
 *
 * Created on 5 de Setembro de 2011, 16:48
 */
#include "bib.h"
//bibliotecas padrão
#include <cstdlib>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>

//bibliotecas para sockets
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

//bibliotecas para string
#include <string>
#include <string.h>
//#define _GNU_SOURCE

// biblioteca para paralelizacao
#include <pthread.h>

//detalhes servidor
#define SERVER "lam(2011)"

//bibliotecas para controle de tempo
#define tipoData "%a, %m %b %Y %X %Z"
#include <sys/stat.h>
#include <sys/types.h>


using namespace std;

/*
 * 
 */
char caminho_base[2048];

int main(int argc, char** argv) {

    if (!argc==3){
        cout<< "Numero de argumentos incompletos, favor digitar o diretorio e a porta do bind"<<endl;
    }

    strcpy(caminho_base,argv[1]);
    int porta = atoi(argv[2]);
    int *porta_em_escuta;
    printf("INICIANDO SERVIDOR\n\n");
    //vamos abrir uma porta para aguardar uma requisicao
    porta_em_escuta=cria_socket(porta);
    printf("\n\n----Aguardando Conexao----\n\n");
    if(listen(*porta_em_escuta, 5) < 0)
        printf("Falha ao tentar escutar o socket do servidor");
    //vamos estabelecer uma conexao na porta aberta
    cria_conexao(porta_em_escuta);
    free(porta_em_escuta);
    printf("ENCERRANDO SERVIDOR\n\n");
    return 0;
}

int *cria_socket(int porta)
{
    int *porta_em_escuta;
    struct sockaddr_in servidor;

    porta_em_escuta = (int *) malloc(sizeof(int));

    // vamos puxar a funcao socket para definir o tipo de protocol
    // 0- para IP , 6 - TCP e 17 para UDP
    // sock_stream para TCP ou sock_dgram para UDP
  if((*porta_em_escuta = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  {
    cout<<"Falha ao criar o socket"<<endl;
    exit(1);
  }

    // vamos agora colocar os dados no servidor

    // vamos limpar a estrutura servidor
    bzero(&servidor, sizeof(servidor));
    
    //definir o ip em que o servidor ira trabalhar
    servidor.sin_addr.s_addr=htonl(INADDR_ANY); //para aceitar qualquer ip

    //vamos definir a familia
    servidor.sin_family=AF_INET;

    // vamos definir a porta
    servidor.sin_port=htons(porta);//converte int para byte de rede tipo tcp/ip

    // vamos fazer o bind
    if(bind(*porta_em_escuta, (struct sockaddr *) &servidor, sizeof(servidor)) < 0)
    {
        cout<<"Nao conseguiu fazer o bind"<<endl;
        exit(1);
    }
    printf("BIND executado com sucesso\n");
    return porta_em_escuta;
}


static void *executa_thread(void *p_tr)
{
    int socket_cliente;
    socket_cliente= *((int *)p_tr);
    pthread_detach(pthread_self());//funcao para liberar quando a thread acaba

    executa(socket_cliente); //executa o pedido
    close(socket_cliente); //fecha a conexao
    return NULL;
}


void cria_conexao(int *porta_em_escuta)
{
    //vamos criar agora um socket para o cliente
    struct sockaddr_in cliente;
    socklen_t tam_cliente;

    //para paralelizacao
    int *p_tr; //ponteiro para trhread
    pthread_t t_id; //identificador da thread

    // para aceitar a conexao
    while(1)
    {
        p_tr = (int *) malloc(sizeof(int));
        *p_tr=accept(*porta_em_escuta, (struct sockaddr *) &cliente, &tam_cliente);
        printf("CONEXAO ACEITA\n");
        pthread_create(&t_id, NULL, &executa_thread, p_tr);
        //vamos agora fazer a execucao do pedido

    }
    
}

void executa(int socket_cliente)
{
    char buffer[1024], metodo[15], uri[1024], http_version[1024];
    int bytes_recebidos;
    char caminho[2048];
    char buf[1024];
    int flag=0;


    //para controle de tempo
    time_t rawtime;
    struct tm *timeinfo, *ltime;
    struct stat arq;

    time(&rawtime);
    timeinfo = localtime(&rawtime);
    char timebuf[50], lastime[50];
    ltime = localtime(&arq.st_mtime);

    strftime(lastime, sizeof(lastime), tipoData, ltime);
    strftime(timebuf, sizeof(timebuf), tipoData, timeinfo);

    //vamos limpar o caminho para nao deixar lixo
    bzero(&caminho, 2048*sizeof(char));

    //vamos usar a funcao recv para ler o protocolo do cliente
    // e armazenar em um buffer a string recebida
    if((bytes_recebidos = recv(socket_cliente, buffer, 1024, 0)) < 0)
    {
        printf("Nao foi possivel ler o cabecalho do cliente\n");
        close(socket_cliente);
        exit(1);
    }
    if(bytes_recebidos == 0)
        printf("conexao finalizada com sucesso\n");

    printf("conteúdo do BUFFER: %s\n", buffer);
     
    //vamos separar os tokens da primeira linha da requisicao
    //SEPARA METODO
    int i=0, j=0;
    while(buffer[i]!=32)
    {
        metodo[j]=buffer[i];
        i++;
        j++;
    }
    metodo[j]='\0';
    j=0;
    i++;

    //SEPARA URI
    if(caminho_base[0]=='/' && caminho_base[1]=='\0')
            i++;
    while(buffer[i]!=32)
    {
        //verifica se a URI solicitada nao estah explorando diretorios nao autorizados
        if ((buffer[i]== '.' && buffer[i+1]== '.')||(buffer[i]==92))
        {
            //mensagem para o servidor avisando a tentativa
            printf("----TENTATIVA DE ACESSO A DIRETORIOS NAO AUTORIZADOS----\n\n");
            //mensagem enviada ao cliente
            strcpy(buf, "HTTP/1.1 404 NOT FOUND\r\n");
            strcat(buf, "Connection: close\r\n");
            strcat(buf, "\r\n");
            strcat(buf, "\r\n");
            strcat(buf, "<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1>");
            strcat(buf,"<p>The requested URL was not found on this server.</p><hr><address>LAM Server");
            strcat(buf,"Port 80</address> </body></html>");
            send(socket_cliente, buf, strlen(buf), 0);
            i++;
            j++;
            printf("----CONEXAO RECUSADA----");
            flag =2;
        }
        else
        {
            uri[j]=buffer[i];
            i++;
            j++;
        }
    }
    uri[j]='\0';
    j=0;
    i++;

    //SEPARA HTTP VERSION
    while(buffer[i]!=32)
    {
        http_version[j]=buffer[i];
        i++;
        j++;
    }
    http_version[j]='\0';

    //vamos verificar a presenca do arquivo
    FILE *fp;
   
    FILE *file;
    long tam_arq, size;
    file = fopen(uri, "rb");
    if(file)
    {
        fseek(file , 0 , SEEK_END);
        tam_arq = ftell(file);
        rewind(file);
        fclose(file);
    }

    char *c;

    char char_size[5] = {0};
    sprintf (char_size,"%d", tam_arq);
    //caso o caminho base seja o diretorio raiz
    if(caminho_base[0]=='/' && caminho_base[1]=='\0' && flag==0)
    {
	fp= fopen(uri, "rb");
        if (fp == NULL)
        {
            strcpy(buf, "HTTP/1.1 404 NOT FOUND\r\n");
            strcat(buf, "Connection: close\r\n");
            strcat(buf, "\r\n");
            strcat(buf, "\r\n");
            strcat(buf, "<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1>");
            strcat(buf,"<p>The requested URL: ");
            strcat(buf, uri);
            strcat(buf," was not found on this server.</p><hr><address>LAM Server");
            strcat(buf,"Port 80</address> </body></html>");
            send(socket_cliente, buf, strlen(buf), 0);
            flag = 2;
        }
    }
    else
    {
        strcat(caminho,caminho_base);
	strcat(caminho,uri);
	fp= fopen(caminho, "rb");
        //server nao possui arquivo
        if (fp == NULL)
        {
            strcpy(buf, "HTTP/1.1 404 NOT FOUND\r\n");
            strcat(buf, "Connection: close\r\n");
            strcat(buf, "\r\n");
            strcat(buf, "\r\n");
            strcat(buf, "<html><head><title>404 Not Found</title></head><body><h1>Not Found</h1>");
            strcat(buf,"<p>The requested URL: ");
            strcat(buf, caminho);
            strcat(buf,"was not found on this server.</p><hr><address>LAM Server");
            strcat(buf,"Port 80</address> </body></html>");
            send(socket_cliente, buf, strlen(buf), 0);
            flag = 2;
        }
            
    }    

    //se o pedido for GET
    if(strcmp(metodo,"GET")==0 && flag ==0)
    {
        //inicio cabecalho da mensagem
        strcpy(buf, "HTTP/1.1 200 OK\r\n");
        strcat(buf, "Connection: close\r\n");
        strcat(buf, "Date: ");
        strcat(buf, timebuf);
        strcat(buf, "\r\n");
        strcat(buf, "Server: ");
        strcat(buf, SERVER);
        strcat(buf, "\r\n");
        strcat(buf, "Content-Length: ");
        strcat(buf, char_size);
        strcat(buf, "\r\n");
        strcat(buf, "Last-Modified: ");
        strcat(buf, lastime);
        strcat(buf, "\r\n");
        strcat(buf, "\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
        //fim cabecalho da mensagem
        //comeco do corpo da mensagem
        if (fp)
        {
            fseek(fp , 0 , SEEK_END);
            size = ftell(fp);
            rewind(fp);
            c = (char*) malloc(sizeof(char) * size);
            fread(c, 1, size, fp);
            send(socket_cliente, c, size, 0);
            fclose(fp);
            free(c);
        }
        //fim do corpo da mensagem
    }

    //se o pedido for HEAD
    else if(strcmp(metodo,"HEAD")==0 && flag ==0)
    {
        //inicio cabecalho da mensagem
        strcpy(buf, "HTTP/1.1 200 OK\r\n");
        strcat(buf, "Connection: close\r\n");
        strcat(buf, "Date: ");
        strcat(buf, timebuf);
        strcat(buf, "\r\n");
        strcat(buf, "Server: ");
        strcat(buf, SERVER);
        strcat(buf, "\r\n");
        strcat(buf, "Content-Length: ");
        strcat(buf, char_size);
        strcat(buf, "\r\n");
        strcat(buf, "Last-Modified: ");
        strcat(buf, lastime);
        strcat(buf, "\r\n");
        strcat(buf, "\r\n");
        send(socket_cliente, buf, strlen(buf), 0);
        //fim cabecalho da mensagem
    }
    //vamos tratar metodos nao implementados
    else if( flag ==0)
    {
     sprintf(buf, "HTTP/1.1 501 Metodo nao implementado\r\n");
     send(socket_cliente, buf, strlen(buf), 0);
     sprintf(buf, "\n\n");
     send(socket_cliente, buf, strlen(buf), 0);
    }
    fclose(fp);
    //fim da comunicacao
    printf("\n\n---- Fim Comunicação ----\n\n");
}


